/**
 * 
 */
package prj3;

/**
 * Implements spherical coordinates (r, THETA, PHI)
 * @author romain
 *
 */
public class SphericalCoordinate implements ICoordinate {


	/**
	 * Constructs a new spherical coordinate with a longitude and latitude
	 * @param grid
	 * @param _r
	 * @param _longitude
	 * @param _latitude
	 */
	public SphericalCoordinate(SphericalGrid grid, float _r, int _longitude, int _latitude) {
		if(_r <= 0 || _longitude < 0 || _latitude < 0 || _longitude > 2*Math.PI/grid.getStepPhi()
				|| _latitude > 2*Math.PI/grid.getStepTheta())
			throw new IllegalArgumentException("Values for spherical coordinate are invalid: latitude = " + _latitude + " (max = " + 2*Math.PI/grid.getStepTheta() + ") longitude = " + _longitude + " (max = " + 2*Math.PI/grid.getStepPhi() + ")");
		radius = _r;
		longitude = _longitude;
		latitude = _latitude;
		theta = latitude * grid.getStepTheta();
		phi = longitude * grid.getStepPhi();
		myGrid = grid;
	}
	
    public SphericalCoordinate(SphericalGrid grid, int _longitude, int _latitude) {
        this(grid, grid.getRadius(), _longitude, _latitude);
    }
    
	/**
	 * Construct a new spherical coordinate point
	 * @param grid SphericalGrid on which this coordinate resides
	 * @param _r radius in meters
	 * @param _t theta in radians (0-PI) from Z to XY plane
	 * @param _p phi in radians (0-2PI) from X to Y in XY plane
	 */
	public SphericalCoordinate(SphericalGrid grid, float _r, float _t, float _p) {
		if(_r <= 0 || _t < 0 || _t > Math.PI || _p < 0 || _p >= 2*Math.PI)
			throw new IllegalArgumentException("Values for spherical coordinates are invalid");
		radius = _r; theta = _t; phi = _p;
		myGrid = grid;
		/* Clamp longitude and latitude. This deals with all the imprecision issues */
		longitude = (int)Math.floor(theta/grid.getStepTheta());
		latitude = (int)Math.floor(phi/grid.getStepPhi());
	}
	
    
    
	/**
	 * Copy constructor
	 * @param coord
	 */
	public SphericalCoordinate(SphericalCoordinate coord) {
		this.theta = coord.theta;
		this.phi = coord.phi;
		this.radius = coord.radius;
		this.myGrid = coord.myGrid;
		this.longitude = coord.longitude;
		this.latitude = coord.latitude;
	}
    
    
	
	/**
	 * Returns the theta value for this point (0-PI)
	 * @return
	 */
	public float getTheta() {
		return theta;
	}
	
	/**
	 * Returns the phi value for this point (0-2PI)
	 * @return
	 */
	public float getPhi() {
		return phi;
	}
	
	/**
	 * Returns the radius for this point
	 * @return
	 */
	public float getRadius() {
		return radius;
	}
	
	/**
	 * Gets the latitude of this point
	 * @return int
	 */
	public int getLatitude() {
		return latitude;
	}
	
	/**
	 * Gets the longitude of this point
	 * @return int
	 */
	public int getLongitude() {
		return longitude;
	}
	
	/* Private data */
	protected float theta, phi, radius;
	protected int longitude, latitude;
	SphericalGrid myGrid;
	
	/* Inherited methods */
	/* (non-Javadoc)
	 * @see prj3.prj2.ICoordinate#getNeighbor(int)
	 */
	public ICoordinate getNeighbor(int direction) {
		return myGrid.getNeighbor(this, direction);
	}

	/* (non-Javadoc)
	 * @see prj3.prj2.ICoordinate#printPosition()
	 */
	public String printPosition() {
		return new String("(" + new Float(radius).toString() + ", " + new Float(theta).toString()
				+ ", " + new Float(phi).toString() + ")");
	}

	public boolean equals(Object obj) {
		if(obj instanceof SphericalCoordinate) {
			SphericalCoordinate toCompare = (SphericalCoordinate)obj;
			return (this.latitude == toCompare.latitude && this.longitude == toCompare.longitude
					&& this.myGrid == toCompare.myGrid);
		}
		return false;
	}

	public int hashCode() {
		return (this.latitude*1000 + this.longitude);
	}

	public boolean isInRange(ICoordinate start, float latRange, float longRange) {
		SphericalCoordinate startCoord = (SphericalCoordinate)start;
		
		if(this.getTheta() > startCoord.getTheta())
			return false; // We are definitely not in the right place
		else {
			float lowerTheta = startCoord.getTheta() - latRange;
			if(lowerTheta <0)
				lowerTheta = 0;
			if(this.getTheta() < lowerTheta)
				return false;
		}
		// Here the latitude checks out
		// Now check the longitude
		float lowerPhi = startCoord.getPhi() - longRange;
		if(lowerPhi < 0) {
			if(this.getPhi() < startCoord.getPhi())
				return true; // we are between 0 and the startCoord
			lowerPhi+= 2*Math.PI;
			if(this.getPhi() >= lowerPhi)
				return true; // We are btw lowerPhi and 0
			return false;
		} else {
			return (this.getPhi() >= lowerPhi && this.getPhi() <= startCoord.getPhi());
		}
	}

	
	
}
